-module(polyanya_nif).

-define(NIF_APP, epolyanya).

-include_lib("eunit/include/eunit.hrl").
-include("polyanya_nif.hrl").

-export([
    load_navmesh/2,
    find_path/3
]).

-define(_assertDelta(Expect, Expr), ?_test(?assertDelta(Expect, Expr))).
-define(assertDelta(Expect, Expr),
    begin
        ((fun () ->
            X__X = (Expect),
            ExprV = (Expr),
            case not ExprV - X__X < 0.001 of
                true ->
                    ?assertEqual(X__X, ExprV);
                _ ->
                    ok
            end
          end)())
    end).

-on_load(init/0).

init() ->
    load_nif_from_crate(polyanya_nif, 0).


load_nif_from_crate(Crate, Init) ->
    Path = filename:join([code:priv_dir(?NIF_APP), "crates", Crate, Crate]),
    erlang:load_nif(Path, Init).

%% @doc 加载navmesh文件
%% MeshType: POLYANYA_NAVMESH_TYPE_XXX
-spec load_navmesh(binary(), MeshType) -> {ok, reference()}.
load_navmesh(NavmeshFile, ?POLYANYA_NAVMESH_TYPE_POLYANYA) ->
    load_polyanya_navmesh(NavmeshFile);
load_navmesh(NavmeshFile, ?POLYANYA_NAVMESH_TYPE_UNITY) ->
    load_unity_navmesh(NavmeshFile).

%% @doc 加载navmesh文件
-spec load_polyanya_navmesh(binary()) -> {ok, reference()}.
load_polyanya_navmesh(_NavmeshFile) ->
    erlang:nif_error(nif_not_loaded).

%% @doc 加载unity navmesh文件
-spec load_unity_navmesh(binary(), MeshType) -> {ok, reference()}.
load_unity_navmesh(?POLYANYA_NAVMESH_TYPE_POLYANYA) ->
    erlang:nif_error(nif_not_loaded).

%% @doc 寻路
-spec find_path(Ref, From, To) -> PathList when
    Ref :: reference(),
    From :: {number(), number(), number()},
    To :: {number(), number(), number()},
    PathList :: [{number(), number(), number()}].
find_path(_Ref, _From, _To) ->
    erlang:nif_error(nif_not_loaded).

%% @doc 寻路测试
path_test_() ->
    Ref = load_navmesh(<<"aurora.mesh">>, ?POLYANYA_NAVMESH_TYPE_POLYANYA),
    io:format("11~n"),
    From = {749.0, 97.0},
    To = {749.0, 104.0},
%%    From = {12.0, 0.0},
%%    To = {3.0, 1.0},
    Path = find_path(Ref, From, To),
    io:format("Path ~p ~n", [Path]),
    Path.


ntime() ->
    {A, B, C} = erlang:timestamp(),
    A * 1000000000000 + B * 1000000 + C.

move_navigator_test() ->
    Ref = load_navmesh(<<"aurora.mesh">>),
    SpawnPos = {548.26666, 325.33334}, % 出生点
    {SpawnPosX, SpawnPosY} = SpawnPos,
    DestPos = {620.8, 205.86668}, % 目标点
    Time = ntime(),
    Path = find_path(Ref, SpawnPos, DestPos),
    io:format("Path ~p ~n", [Path]),
    {MeshSizeX, MeshSizeY} = {1024, 768},
    Factor = 0.9375,
    {PosX, PosY} = {(SpawnPosX - MeshSizeX / 2.0) *Factor, (SpawnPosY - MeshSizeY / 2.0) * Factor},
    io:format("PosX, PosY ~p ~n", [{PosX, PosY}]),
    Fun =
        fun() ->
            test_move_navigator_loop(Time, PosX, PosY, Path)
        end,
    Pid = erlang:spawn(Fun),
    timer:send_interval(100, Pid, update),
    ok.

normalize_pos(PosX, PosY) ->
    Len = math:sqrt(PosX * PosX + PosY * PosY),
    LengthRecip = 1.0 / Len,
    {PosX * LengthRecip, PosY * LengthRecip}.


test_move_navigator_loop(Time, PosX, PosY, [Path | LeftPath]) ->
    receive
        update ->
            Factor = 0.9375,
            Speed = 100,
            NowTime = ntime(),
            TimeDiff = NowTime - Time,
            case TimeDiff < 0 of
                true ->
                    io:format("TimeDiff ~p NowTime ~p Time ~p ~n", [TimeDiff, NowTime, Time]),
                    test_move_navigator_loop(Time, PosX, PosY, [Path | LeftPath]);
                _ ->
                    TimeDiffSec = TimeDiff / 1000000,
                    {MeshSizeX, MeshSizeY} = {1024, 768},
                    {PathX, PathY} = Path,
                    {NextX, NextY} = Next = {(PathX - MeshSizeX / 2.0) *Factor, (PathY - MeshSizeY / 2.0) * Factor},
                    {TowardX, TowardY} = Toward = {NextX - PosX, NextY - PosY},
                    Len = math:sqrt(TowardX * TowardX + TowardY * TowardY),
                    {NorTowardX, NorTowardY} = normalize_pos(TowardX, TowardY),
                    {AddPosX, AddPosY} = {TimeDiffSec * NorTowardX * Speed, TimeDiffSec * NorTowardY * Speed},
                    {NewPosX, NewPosY} = {PosX + AddPosX, PosY + AddPosY},
                    io:format("NewPos ~p~n", [{NewPosX, NewPosY}]),
%%                    io:format("Path ~p Next ~p Toward ~p TimeDiffSec ~p Pos ~p NewPos ~p NorNor ~p ~n", [Path, Next, Toward, TimeDiffSec, {PosX, PosY}, {NewPosX, NewPosY}, {NorTowardX, NorTowardY}]),
                    case Len < TimeDiffSec * Speed of
                        true ->
                            case LeftPath of
                                [_ | _] ->
                                    io:format("==================== reached next step ~n"),
                                    test_move_navigator_loop(NowTime, NewPosX, NewPosY, LeftPath);
                                _ ->
                                    io:format("============= reached target ~n")
                            end;
                        _ ->
                            test_move_navigator_loop(NowTime, NewPosX, NewPosY, [Path | LeftPath])
                    end
            end
    end.